#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>

#define DTSLED_CNT      1
#define DTSLED_NAME     "myled"

/*  下面这个几个类型由于内核没有提供给client device driver使用，因此我们只能自己定义 */
struct pinctrl_setting_mux {
        unsigned group;
        unsigned func;
};
struct pinctrl_setting_configs {
        unsigned group_or_pin;
        unsigned long *configs;
        unsigned num_configs;
};
struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;
    struct pinctrl_dev *pctldev;
    const char *dev_name;
    union {
        struct pinctrl_setting_mux mux;   // mux配置数据
        struct pinctrl_setting_configs configs;  // config配置数据
    } data;
};
struct pinctrl_state {
        struct list_head node;
        const char *name;
        struct list_head settings;
};

/* 定义一个led设备 */
struct led_dev myled;

/* 定义led结构体 */
struct led_dev {
    dev_t devid;                   /* 字符设备编号 */
    struct cdev cdev;              /* 保存操作结构体的字符设备 */
    struct class *class;           /* class */
    struct device *device;         /* 设备类 */
    struct device_node *nd;        /* 设备节点 */
    struct pinctrl *pinctrl;       /* pin control state holder */
    struct pinctrl_state *state;   /* 当前pin control state */
};

static int led_open(struct inode *inode, struct file *file)
{
    return 0;
}

/* 点亮/熄灭 LED01 */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int val,ret;

    copy_from_user(&val, buf, count);   // 用户空间到内核空间传递数据

    printk("value %d",val);

    if(val == 1){
        /* 点亮 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
        if (IS_ERR(myled.state)) {
            dev_dbg(myled.device, "no default pinctrl state\n");
            ret = PTR_ERR(myled.state);
            return ret;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return ret;
        }else{
            printk("pinctrl select state success\n");
        }
    }
    else{
        /* 熄灭 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
        if (IS_ERR(myled.state)) {
            dev_dbg(myled.device, "no myled_off pinctrl state\n");
            ret = PTR_ERR(myled.state);
            return ret;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return ret;
        }else{
            printk("pinctrl select state success\n");
        }
    }

    return 0;
}

static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,
    .open   =   led_open,
    .write  =   led_write,
};

/*
 * 当驱动和硬件信息匹配成功之后，就会调用probe函数，驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int led_probe(struct platform_device *pdev)
{
      int ret = 0,i = 0;
      struct pinctrl_setting *setting;
      struct device_node *np;
      const char *statename;
      struct dev_pin_info *pins;

      printk("%s enter.\n", __func__);


      if(pdev->name != NULL){
            printk("platform device name %s",pdev->name);   // myled
      }

      /* 获取设备引脚状态信息 */
      np = pdev->dev.of_node;
      ret = of_property_read_string_index(np, "pinctrl-names", 0, &statename);  // 读取pinctrl-names属性第0个值
      if (ret == 0) {
           printk("pinctrl-names index 0 value %s",statename);
      }

       /* 1.获取与设备相关联的pinctrl句柄 */
       pins = pdev->dev.pins;
       myled.pinctrl = pins->p;
       if (IS_ERR(myled.pinctrl)){
            printk("retrieves the pinctrl handle for a device failed\n");
            ret = PTR_ERR(myled.pinctrl);
            goto faile_devid;
       }else{
            printk("retrieves the pinctrl handle for a device success\n");
       }

       /* 2. 获取指定的name的state */
       myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
       if (IS_ERR(myled.state)){
           printk("pinctrl lookup state failed\n");
           ret = PTR_ERR(myled.state);
           goto faile_devid;
       }else{
           printk("pinctrl lookup state success\n");
       }

        /*3、设置引脚状态 */
       ret = pinctrl_select_state(myled.pinctrl, myled.state);
       if (ret < 0){
           printk("pinctrl select state failed\n");
           goto faile_devid;
       }else{
           printk("pinctrl select state success\n");
       }

       /* 输出状态state以及状态setting信息 */
       if(myled.state->name != NULL){
            printk("state name %s", myled.state->name);    // 状态名称为default
       }

        /* 遍历当前状态下的所有setting */
       list_for_each_entry(setting, &(myled.state->settings), node) {
               printk("setting type %d", setting->type);
               printk("setting dev_name %s", setting->dev_name);    // 设备名称为myled
               if(setting->type == PIN_MAP_TYPE_MUX_GROUP ){        // 引脚复用 枚举值为2
                     printk("setting mux group %d", setting->data.mux.group);
                     printk("setting mux func %d", setting->data.mux.func);
               }else{                                              // 配置引脚电气特性
                    printk("--------------configs start--------");
                    printk("setting configs group_or_pin %d", setting->data.configs.group_or_pin);
                    for(i=0; i<setting->data.configs.num_configs; i++){
                        printk("setting configs configs %d", setting->data.configs.configs[i]);
                    }
                    printk("--------------configs end--------");
               }
        }

       /* 4. 动态分配字符设备号 */
       ret = alloc_chrdev_region(&myled.devid, 0, 1,DTSLED_NAME);  // ls /proc/devices看到的名字
       /* 返回值为负数，表示操作失败 */
       if (ret < 0) {
           printk("alloc char device region failed\n");
           goto faile_devid;
       }else{
           printk("alloc char device region success\n");
       }

     /* 5.初始化字符设备，添加字符设备 */
      cdev_init(&myled.cdev, &led_fops);
      ret = cdev_add(&myled.cdev, myled.devid, DTSLED_CNT);
      /* 返回值为负数，表示操作失败 */
      if (ret < 0) {
          printk("char device add failed\n");
          goto fail_cdev;
      }else{
          printk("char device add success\n");
      }

    /* 6.创建类,它会在sys目录下创建/sys/class/dtsled这个类  */
     myled.class = class_create(THIS_MODULE, DTSLED_NAME);
     if(IS_ERR(myled.class)){
         printk("create class failed\n");
         ret = PTR_ERR(myled.class);
         goto fail_class;
     }else{
          printk("create class success\n");
      }

    /* 7. 在/sys/class/led下创建dtsled设备，然后mdev通过这个自动创建/dev/dtsled这个设备节点 */
     myled.device = device_create(myled.class, NULL, myled.devid, NULL, DTSLED_NAME);
     if(IS_ERR(myled.device)){
         printk("create device failed\n");
         ret = PTR_ERR(myled.device);
         goto fail_device;
     }else{
         printk("create device success\n");
    }

     return 0;

fail_findnd:
    device_destroy(myled.class, myled.devid);
fail_device:
    class_destroy(myled.class);
fail_class:
    cdev_del(&myled.cdev);
fail_cdev:
    unregister_chrdev_region(myled.devid, DTSLED_CNT);
faile_devid:
    return ret;
}

/*
 * 硬件信息被移除了，或者驱动被卸载了，全部要释放，释放资源的操作就放在该函数中
 */
static int led_remove(struct platform_device * pdev)
{
    printk("led driver exit\n");

    /* 注销类、以及类设备 */
    device_destroy(myled.class, myled.devid);
    class_destroy(myled.class);

    /* 删除设备，卸载注册的设备编号 */
    cdev_del(&myled.cdev);
    unregister_chrdev_region(myled.devid, DTSLED_CNT);

    return 0;
}


/*
 * 用于设备树匹配
 */
static const struct of_device_id led_dt_match[] = {
    { .compatible = DTSLED_NAME, },
    {},
};

/*
 * platform驱动
 */
static struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,

    .driver = {
        .name = DTSLED_NAME,
        .of_match_table = led_dt_match,  // 匹配列表
    }
};

/*
 * platform驱动模块入口
 */
static int led_drv_init(void)
{
   // platform驱动注册
   int err = platform_driver_register(&led_driver);
   if (err) {
          printk("platform driver registered failed\n");
   } else {
          printk("platform driver registered successfully\n");
   }
   return err;
}

/*
 * platform驱动模块出口
 */
static void __exit led_drv_exit(void)
{
    printk("platform driver unregistered\n");
    // platform驱动卸载
    platform_driver_unregister(&led_driver);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");